package cz.cuni.amis.episodic.lisp.netcreators;

import java.io.*;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import cz.cuni.amis.episodic.lisp.LispPlanToCsvData;
import cz.cuni.amis.episodic.lisp.LispPlanToCsvData.BehanFilteredProcessor;
import cz.cuni.amis.episodic.lisp.LispPlanToCsvData.LispToCsvResult;
import cz.cuni.amis.episodic.lisp.LispPlanToCsvData.TreeToTraceTranslator;
import jsmile.utils.CsvToSmileData;
import jsmile.utils.Learn;
import lombok.Cleanup;
import lombok.Data;
import lombok.Setter;
import smile.Network;

/**
 *
 * @author ik
 */
@Data
public abstract class NetCreator {
	

    String filenameSuffix = "";
    /**
     * Length of examples used for training.
     */
    protected int batchSize = 6;
    
    //LispPlanToCsvData.TreeToTraceTranslator translator = new LispPlanToCsvData.BehanFilteredProcessor(3);
    LispPlanToCsvData.TreeToTraceTranslator translator = new LispPlanToCsvData.BehanFilteredProcessor(10);

    @Setter
    protected TraceModificationStrategy traceModificationStrategy;
     
    public List<List<String>> lispToTrainingData(File inputLispPlan, File smileTrainingData) throws IOException {
        // get filename without path and extension

        // read lisp to csv
        ByteArrayOutputStream csvOs = new ByteArrayOutputStream();
        LispPlanToCsvData.LispToCsvResult res = LispPlanToCsvData.lispToCsv(inputLispPlan.getAbsolutePath(), csvOs, batchSize, translator, traceModificationStrategy);
        csvOs.close();

        // transform csv to smile data format
        CsvToSmileData.convert(new InputStreamReader(new ByteArrayInputStream(csvOs.toByteArray())),
                smileTrainingData.getAbsolutePath());

        // write also the csv
        String smilePath = smileTrainingData.getAbsolutePath();
        File csvFile = new File(smilePath.substring(0, smilePath.lastIndexOf('.') + 1) + "csv");
        @Cleanup
        FileOutputStream fos = new FileOutputStream(csvFile);
        fos.write(csvOs.toByteArray());

        return res.levelActions;
    }

    /**
     * Creates network structure.
     *
     * @param levelActions possible node outcomes on each level of hierarchy
     * @return
     */
    public abstract Network createNet(List<List<String>> levelActions);

    public Network createAndLearnNetwork(File inputLispPlan, File outputDirectory, int[] range) throws IOException {
        outputDirectory.mkdirs();

        // some naming conventions
        String corporaName = inputLispPlan.getName().split("\\.")[0];
        String smileDataFilename = corporaName + filenameSuffix + "_smile_data.txt";
        File smileDataFile = new File(outputDirectory, smileDataFilename);

        // read lisp
        // convert it to the training data
        List<List<String>> levelActions = lispToTrainingData(inputLispPlan, smileDataFile);

       
        
        // create network structure
        Network net = createNet(levelActions);

        // learn the net parameters
        Learn.learnEM(net, smileDataFile.getAbsolutePath(), range);

        // set some info
        net.setDescription("Goal recognition network created from LISP plan file "
                + inputLispPlan.getAbsolutePath());
        net.setName("Goal recognition for " + inputLispPlan.getName());

        File netFile = new File(outputDirectory, corporaName + filenameSuffix + ".xdsl");

        // write the net
        net.writeFile(netFile.getAbsolutePath());

        return net;
    }

    public void writeNet(String networkFileName, List<List<String>> levelActions) {
        Network net = createNet(levelActions);
        net.writeFile(networkFileName + filenameSuffix + ".xdsl");
    }

    static void addTemporalNode(Network net, String name) {
        net.addNode(Network.NodeType.Cpt, name);
        net.setNodeTemporalType(name, Network.NodeTemporalType.Plate);
    }

    /**
     *
     * @param net Network where the node will be added
     * @param name Name of the node
     * @param outcomes Collection of outcome/state names
     */
    static void addNode(Network net, String name, Collection<String> outcomes) {
        addTemporalNode(net, name);

        for (String outcome : outcomes) {
            //net.setOutcomeId(name, outcomeId++, outcome); 
            net.addOutcome(name, outcome);
        }
    }

    static void adjustNodes(Network net, String[] nodes, boolean observationTemporalModel) {
        // values for layout
        int x = 180;
        int y = 60;
        int width = 80;
        int height = 60;
        for (String node : nodes) {
            if (!(observationTemporalModel == false && node.equals("O"))) {
                //net.setNodeTemporalType(node, Network.NodeTemporalType.Plate);
                net.addTemporalArc(node, node, 1);
            }
            // delete default outcomes
            deleteDefaultOutcomes(net, node);

            //net.deleteOutcome(node, 0);
            //net.deleteOutcome(node, 1);

            // do layouting
            net.setNodePosition(node, x, y, width, height);
            y += 80;
        }
    }

    protected static void deleteDefaultOutcomes(Network net, String node) {
        net.deleteOutcome(node, "State0");
        net.deleteOutcome(node, "State1");
    }
}
